home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 21
/
Aminet 21 (1997)(GTI - Schatztruhe)[!][Oct 1997].iso
/
Aminet
/
gfx
/
x11
/
xmountains.lha
/
src
/
calcalt.c
< prev
next >
Wrap
C/C++ Source or Header
|
2003-06-12
|
17KB
|
777 lines
/*
* Recursive update procedure for fractal landscapes
*
* The only procedures needed outside this file are
* make_fold, called once to initialise the data structs.
* next_strip, each call returns a new strip off the side of the surface
* you can keep calling this as often as you want.
* free_strip, get rid of the strip when finished with it.
* free_fold, get rid of the data structs when finished with this surface.
*
* Apart from make_fold all these routines get their parameters from their
* local Fold struct, make_fold initialises all these values and has to
* get it right for the fractal to work. If you want to change the fractal
* dim in mid run you will have to change values at every level.
* each recursive level only calls the level below once for every two times it
* is called itself so it will take a number of iterations for any changes to
* be notices by the bottom (long length scale) level.
*/
#include <stdio.h>
#include <math.h>
#include "crinkle.h"
char calcalt_Id[] = "$Id: calcalt.c,v 2.6 1995/06/12 18:58:04 spb Exp spb $";
#ifdef DEBUG
#define DB(A,B) dump_pipeline(A,B)
#else
#define DB(A,B)
#endif
/*{{{ Strip *make_strip(int level) */
Strip *make_strip (level)
int level;
{
Strip *p;
int points;
p = (Strip *) malloc( sizeof(Strip) );
if( p == NULL )
{
fprintf(stderr,"make_strip: malloc failed\n");
exit(1);
}
p->level = level;
points = (1 << level) +1;
p->d = (Height *)malloc( points * sizeof(Height) );
if( p->d == NULL )
{
fprintf(stderr,"make_strip: malloc failed\n");
exit(1);
}
return(p);
}
/*}}}*/
/*{{{ void free_strip(Strip *p) */
void free_strip (p)
Strip *p;
{
if( p->d )
{
free(p->d);
p->d = NULL;
}
free(p);
}
/*}}}*/
/*{{{ Strip *double_strip(Strip s) */
Strip *double_strip (s)
Strip *s;
{
Strip *p;
Height *a, *b;
int i;
p = make_strip((s->level)+1);
a = s->d;
b = p->d;
for(i=0; i < (1<<s->level); i++)
{
*b = *a;
a++;
b++;
*b = 0.0;
b++;
}
*b = *a;
return(p);
}
/*}}}*/
/*{{{ Strip *set_strip(int level, Height value) */
Strip *set_strip (level,value)
int level;
Height value;
{
int i;
Strip *s;
Height *h;
s = make_strip(level);
h = s->d;
for( i=0 ; i < ((1<<level)+1) ; i++)
{
*h = value;
h++;
}
return(s);
}
/*}}}*/
/*{{{ Fold *make_fold(Parm *param, int levels, int stop, Length len) */
/*
* Initialise the fold structures.
* As everything else reads the parameters from their fold
* structs we need to set these here,
* p is the parameter struct common to all update levels.
* levels is the number of levels of recursion below this one.
* Number of points = 2^levels+1
* stop is the number of levels that are generated as random offsets from a
* constant rather than from an average.
* fractal_start, if true we start in the middle of a mountain range
* if false we build up mountains from the start height.
* length is the length of the side of the square at this level.
* N.B this means the update square NOT the width of the fractal.
* len gets smaller as the level increases.
* start, the starting height for a non-fractal start.
*/
Fold *make_fold (param,levels,stop,length)
struct parm *param;
int levels;
int stop;
Length length;
{
Fold *p;
Length scale, midscale;
double root2;
int i;
if( (levels < stop) || (stop<0) )
{
fprintf(stderr,"make_fold: invalid parameters\n");
fprintf(stderr,"make_fold: levels = %d , stop = %d \n",levels,stop);
exit(1);
}
p = (Fold *)malloc(sizeof(Fold));
if( p == NULL )
{
fprintf(stderr,"make_fold: malloc failed\n");
exit(1);
}
root2=sqrt((double) 2.0 );
scale = pow((double) length, (double) (2.0 * param->fdim));
midscale = pow((((double) length)*root2), (double) (2.0 * param->fdim));
p->level = levels;
p->stop = stop;
p->state = START;
p->save =NULL;
p->p = param;
p->scale = scale;
p->midscale = midscale;
for(i=0;i<NSTRIP;i++)
{
p->s[i] = NULL;
}
if( levels > stop )
{
p->next = make_fold(param,(levels-1),stop,(2.0*length));
}else{
p->next = NULL;
}
return( p );
}
/*}}}*/
/*{{{ void free_fold(Fold *f) */
void free_fold (f)
Fold *f;
{
int i;
for(i=0;i<NSTRIP;i++)
{
if( f->s[i] != NULL )
{
free_strip(f->s[i]);
f->s[i] = NULL;
}
}
free(f);
return;
}
/*}}}*/
/*{{{ Strip *next_strip(Fold *fold) */
Strip *next_strip (fold)
Fold *fold;
{
Strip *result=NULL;
Strip *tmp;
Strip **t;
int i, count, iter;
count = ((1 << fold->level) +1);
if( fold->level == fold->stop)
{
/*{{{ generate values from scratch */
result=make_strip(fold->stop);
for( i=0 ; i < count ; i++)
{
result->d[i] = fold->p->mean + (fold->scale * gaussian());
}
/*}}}*/
}else{
/*
* There are two types of strip,
* A strips - generated by the lower recursion layers.
* these contain the corner points and half the side points
* B strips - added by this layer, this contains the mid points and
* half the side points.
*
* The various update routines test for NULL pointer arguments so
* that this routine will not fail while filling the pipeline.
*/
while( result == NULL )
{
/*{{{iterate*/
switch(fold->state)
{
case START:
/*{{{ perform an update. return first result*/
DB("S1",fold);
t=fold->s;
/* read in a new A strip at the start of the pipeline */
tmp =next_strip(fold->next);
t[0] = double_strip(tmp);
free_strip(tmp);
/* make the new B strip */
t[1]=set_strip(fold->level,0.0);
if( ! t[2] )
{
/* we want to have an A B A pattern of strips at the
* start of the pipeline.
* force this when starting the pipe
*/
t[2]=t[0];
tmp =next_strip(fold->next);
t[0] = double_strip(tmp);
free_strip(tmp);
}
DB("E1",fold);
/*
* create the mid point
* t := A B A
*/
DB("S2",fold);
x_update(count,fold->midscale,0.0,t[0],t[1],t[2]);
DB("E2",fold);
if(fold->p->rg1)
{
DB("S3",fold);
/*
* first possible regeneration step
* use the midpoints to regenerate the corner values
* increment t by 2 so we still have and A B A pattern
*/
v_update(count,fold->midscale,fold->p->midmix,t[1],t[2],t[3]);
t+=2;
DB("E3",fold);
}
/*
* fill in the edge points
* increment t by 2 to preserve the A B A pattern
*/
DB("S4",fold);
if( fold->p->cross )
{
t_update(count,fold->scale,0.0,t[0],t[1],t[2]);
p_update(count,fold->scale,0.0,t[1],t[2],t[3]);
t+=2;
}else{
hside_update(count,fold->scale,0.0,t[0],t[1],t[2]);
vside_update(count,fold->scale,0.0,t[2]);
t+=2;
}
DB("E4",fold);
if(fold->p->rg2)
{
DB("S5",fold);
/*
* second regeneration step update midpoint
* from the new edge values
*/
if( fold->p->cross )
{
p_update(count,fold->scale,fold->p->mix,t[0],t[1],t[2]);
}else{
vside_update(count,fold->scale,fold->p->mix,t[1]);
}
DB("E5",fold);
}
/* increment t by 1
* this gives a B A B pattern to regen-3
* if regen 3 is not being used it leaves t pointing to the
* 2 new result strips
*/
t++;
if(fold->p->rg3)
{
DB("S6",fold);
/* final regenration step
* regenerate the corner points from the new edge values
* this needs a B A B pattern
* leave t pointing to the 2 new result strips
*
* this has to be a t_update
*/
t_update(count,fold->scale,fold->p->mix,t[0],t[1],t[2]);
t++;
DB("E6",fold);
}
result=t[1];
fold->save=t[0];
t[0]=t[1]=NULL;
fold->state = STORE;
break;
/*}}}*/
case STORE:
/*{{{ return second value from previous update. */
result = fold->save;
fold->save=NULL;
for(i=NSTRIP-1;i>1;i--)
{
fold->s[i] =fold->s[i-2];
}
fold->s[0] = fold->s[1]=NULL;
fold->state = START;
break;
/*}}}*/
default:
fprintf(stderr,"next_strip: invalid state level %d state %d\n",
fold->level,fold->state);
exit(3);
}
/*}}}*/
}
}
iter = fold->level - fold->stop;
if( fold->p->force_front > iter){
result->d[0] = fold->p->forceval;
}
if( fold->p->force_back > iter){
result->d[count-1] = fold->p->forceval;
}
return(result);
}
/*}}}*/
/*{{{void x_update(int count,float scale, float mix, Strip *a, Strip *b, Strip *c)*/
void x_update(count, scale, mix, a, b, c)
int count;
float scale;
float mix;
Strip *a;
Strip *b;
Strip *c;
{
int i;
float w;
Height *mp, *lp, *rp;
/* don't run unless we have all the parameters */
if( !a || !c ) return;
if( !b )
{
fprintf(stderr,"x_update: attempt to update NULL strip\n");
exit(1);
}
w = (1.0 - mix)/4.0;
mp=b->d;
lp=a->d;
rp=c->d;
if( mix <= 0.0 ){
/*{{{random offset to average of new points*/
for(i=0; i<count-2; i+=2)
{
mp[1] = 0.25 * ( lp[0] + rp[0] + lp[2] + rp[2])
+ (scale * gaussian());
mp+=2;
lp+=2;
rp+=2;
}
/*}}}*/
}else if( mix >= 1.0 ){
/*{{{random offset to old value*/
for(i=0; i<count-2; i+=2)
{
mp[1] = mp[1]
+ (scale * gaussian());
mp+=2;
lp+=2;
rp+=2;
}
/*}}}*/
}else{
/*{{{mixed update*/
for(i=0; i<count-2; i+=2)
{
mp[1] = (mix * mp[1]) + w * ( lp[0] + rp[0] + lp[2] + rp[2])
+ (scale * gaussian());
mp+=2;
lp+=2;
rp+=2;
}
/*}}}*/
}
}
/*}}}*/
/*{{{void p_update(int count,float scale, float mix, Strip *a, Strip *b, Strip *c)*/
void p_update(count, scale, mix, a, b, c)
int count;
float scale;
float mix;
Strip *a;
Strip *b;
Strip *c;
{
int i;
float w;
Height *mp, *lp, *rp;
/* don't run if we have no parameters */
if( !a || !b ) return;
/* if c is missing we can do a vside update instead
* should really be a sideways t but what the heck we only
* need this at the start
*/
if( !c )
{
vside_update(count,scale,mix,b);
return;
}
w = (1.0 - mix)/4.0;
mp=b->d;
lp=a->d;
rp=c->d;
if( mix <= 0.0 ){
/*{{{random offset to average of new points*/
for(i=0; i<count-2; i+=2)
{
mp[1] = 0.25 * ( lp[1] + rp[1] + mp[0] + mp[2] )
+ (scale * gaussian());
mp+=2;
lp+=2;
rp+=2;
}
/*}}}*/
}else if(mix >= 1.0){
/*{{{random offset to old values*/
for(i=0; i<count-2; i+=2)
{
mp[1] = mp[1]
+ (scale * gaussian());
mp+=2;
lp+=2;
rp+=2;
}
/*}}}*/
}else{
/*{{{mixed update*/
for(i=0; i<count-2; i+=2)
{
mp[1] = (mix * mp[1]) + w * ( lp[1] + rp[1] + mp[0] + mp[2] )
+ (scale * gaussian());
mp+=2;
lp+=2;
rp+=2;
}
/*}}}*/
}
}
/*}}}*/
/*{{{void t_update(int count,float scale, float mix, Strip *a, Strip *b, Strip *c)*/
void t_update(count, scale, mix, a, b, c)
int count;
float scale;
float mix;
Strip *a;
Strip *b;
Strip *c;
{
int i;
float w, we;
Height *mp, *lp, *rp;
/* don't run unless we have all the parameters */
if( !a || !c ) return;
if( !b )
{
fprintf(stderr,"t_update: attempt to update NULL strip\n");
exit(1);
}
w = (1.0 - mix)/4.0;
we = (1.0 - mix)/3.0;
mp=b->d;
lp=a->d;
rp=c->d;
if( mix <= 0.0){
/*{{{random offset to average of new points*/
mp[0] = (1.0/3.0) * ( lp[0] + rp[0] + mp[1] )
+ (scale * gaussian());
mp++;
lp++;
rp++;
for(i=1; i<count-3; i+=2)
{
mp[1] = 0.25 * ( lp[1] + rp[1] + mp[0] + mp[2] )
+ (scale * gaussian());
mp+=2;
lp+=2;
rp+=2;
}
mp[1] = (1.0/3.0) * ( lp[1] + rp[1] + mp[0] )
+ (scale * gaussian());
/*}}}*/
}else if(mix >= 1.0){
/*{{{random offset to old values*/
for(i=0; i<count-2; i+=2)
{
mp[0] = mp[0]
+ (scale * gaussian());
mp+=2;
lp+=2;
rp+=2;
}
/*}}}*/
}else{
/*{{{mixed update*/
mp[0] = (mix * mp[0]) + we * ( lp[0] + rp[0] + mp[1] )
+ (scale * gaussian());
mp++;
lp++;
rp++;
for(i=1; i<count-3; i+=2)
{
mp[1] = (mix * mp[1]) + w * ( lp[1] + rp[1] + mp[0] + mp[2] )
+ (scale * gaussian());
mp+=2;
lp+=2;
rp+=2;
}
mp[1] = (mix * mp[1]) + we * ( lp[1] + rp[1] + mp[0] )
+ (scale * gaussian());
/*}}}*/
}
}
/*}}}*/
/*{{{void v_update(int count,float scale, float mix, Strip *a, Strip *b, Strip *c)*/
void v_update(count, scale, mix, a, b, c)
int count;
float scale;
float mix;
Strip *a;
Strip *b;
Strip *c;
{
int i;
float w, we;
Height *mp, *lp, *rp;
/* don't run unless we have all the parameters */
if( !a || !c ) return;
if( !b )
{
fprintf(stderr,"v_update: attempt to update NULL strip\n");
exit(1);
}
w = (1.0 - mix)/4.0;
we = (1.0 - mix)/2.0;
mp=b->d;
lp=a->d;
rp=c->d;
if( mix <= 0.0){
/*{{{random offset of average of new points*/
mp[0] = 0.5 * ( lp[1] + rp[1] )
+ (scale * gaussian());
mp++;
lp++;
rp++;
for(i=1; i<count-3; i+=2)
{
mp[1] = 0.25 * ( lp[0] + rp[0] + lp[2] + rp[2] )
+ (scale * gaussian());
mp+=2;
lp+=2;
rp+=2;
}
mp[1] = 0.5 * ( lp[0] + rp[0] )
+ (scale * gaussian());
/*}}}*/
}else if(mix >= 1.0){
/*{{{random offset to old values*/
for(i=0; i<count-2; i+=2)
{
mp[0] = mp[0]
+ (scale * gaussian());
mp+=2;
lp+=2;
rp+=2;
}
/*}}}*/
}else{
/*{{{mixed update*/
mp[0] = (mix * mp[0]) + we * ( lp[1] + rp[1] )
+ (scale * gaussian());
mp++;
lp++;
rp++;
for(i=1; i<count-3; i+=2)
{
mp[1] = (mix * mp[1]) + w * ( lp[0] + rp[0] + lp[2] + rp[2] )
+ (scale * gaussian());
mp+=2;
lp+=2;
rp+=2;
}
mp[1] = (mix * mp[1]) + we * ( lp[0] + rp[0] )
+ (scale * gaussian());
/*}}}*/
}
}
/*}}}*/
/*{{{void vside_update(int count,float scale, float mix, Strip *a)*/
void vside_update(count, scale, mix, a)
int count;
float scale;
float mix;
Strip *a;
{
int i;
float w;
Height *mp;
/* don't run unless we have all the parameters */
if( !a ) return;
w = (1.0 - mix)/2.0;
mp=a->d;
if( mix <= 0.0){
/*{{{random offset to average of new points*/
for(i=0; i<count-2; i+=2)
{
mp[1] = 0.5 * ( mp[0] + mp[2] )
+ (scale * gaussian());
mp+=2;
}
/*}}}*/
}else if(mix >= 1.0){
/*{{{random offset to old values*/
for(i=0; i<count-2; i+=2)
{
mp[1] = mp[1]
+ (scale * gaussian());
mp+=2;
}
/*}}}*/
}else{
/*{{{mixed update*/
for(i=0; i<count-2; i+=2)
{
mp[1] = (mix * mp[1]) + w * ( mp[0] + mp[2] )
+ (scale * gaussian());
mp+=2;
}
/*}}}*/
}
}
/*}}}*/
/*{{{void hside_update(int count,float scale, float mix, Strip *a, Strip *b, Strip *c)*/
void hside_update(count, scale, mix, a, b, c)
int count;
float scale;
float mix;
Strip *a;
Strip *b;
Strip *c;
{
int i;
float w;
Height *mp, *lp, *rp;
/* don't run unless we have all the parameters */
if( !a || !c ) return;
if( !b )
{
fprintf(stderr,"x_update: attempt to update NULL strip\n");
exit(1);
}
w = (1.0 - mix)/2.0;
mp=b->d;
lp=a->d;
rp=c->d;
if( mix <= 0.0 ){
/*{{{random offset to average of new points*/
for(i=0; i<count; i+=2)
{
mp[0] = 0.5 * ( lp[0] + rp[0] )
+ (scale * gaussian());
mp+=2;
lp+=2;
rp+=2;
}
/*}}}*/
}else if(mix >= 1.0){
/*{{{random offset to old points*/
for(i=0; i<count; i+=2)
{
mp[0] = mp[0]
+ (scale * gaussian());
mp+=2;
lp+=2;
rp+=2;
}
/*}}}*/
}else{
/*{{{mixed update*/
for(i=0; i<count; i+=2)
{
mp[0] = (mix * mp[0]) + w * ( lp[0] + rp[0] )
+ (scale * gaussian());
mp+=2;
lp+=2;
rp+=2;
}
/*}}}*/
}
}
/*}}}*/
#ifdef DEBUG
dump_pipeline(s, f)
char *s;
Fold *f;
{
printf("%s[%d]: %0xd %0xd %0xd %0xd %0xd %0xd %0xd %0xd\n",s,f->level,
f->s[0],f->s[1],f->s[2],f->s[3],f->s[4],f->s[5],f->s[6],f->s[7]);
}
#endif